#include <iostream>
#include <unistd.h>

#include "com_arcvideo_ocean_vag_jni_VagApi.h"
#include "dauproxyapi.h"
#include "vagsdk_util.h"
#include "logger.h"


JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_8) != JNI_OK) {
        return JNI_EVERSION;
    }

    int code = DauProxyApi::GetInstance()->Init();
    if (code != 0) {
        PrintLog("Failed initialize dauproxy library, error code=%d\n", code);
        return JNI_ERR;
    }

    JNIHandle::GetInstance()->Init(env);

    return JNI_VERSION_1_8;
}

JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_8) != JNI_OK) {
        return;
    }
    int code = DauProxyApi::GetInstance()->ProxyReleaseLibrary();
    if (code < 0) {
        PrintLog("ProxyReleaseLibrary return error\n");
    }
}


JNIEXPORT jstring JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_addDevice
        (JNIEnv *env, jclass clz, jobject deviceInfo) {
    ParseDeviceInfoRs rs = ParseDeviceInfo(env, deviceInfo, true);
    RETURN_V_IF_FALSE(rs, nullptr);
    _proxy_dev_info devInfo = rs.info;
    PrintLog("Ready call ProxyAddDevice with: ipaddr=%s, username=%s, password=%s, port=%d, devicetype=%d, "
             "link_mode=%d, uuid=%s\n",
             devInfo.ipaddr.c_str(), devInfo.username.c_str(), devInfo.password.c_str(),
             devInfo.port, devInfo.devicetype, devInfo.link_mode, rs.uuid.c_str());
    int code = DauProxyApi::GetInstance()->ProxyAddDevicePlus(devInfo, rs.uuid);
    PrintLog("addDevice--------------> code: code=%d\n", code);
    if (code > 0) {
        SetObjectStringField(env, deviceInfo, JNIHandle::GetInstance()->fid_deviceInfo_dev_uuid, rs.uuid);
        return env->NewStringUTF(rs.uuid.c_str());
    }
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxyAddDevice return error");
}


JNIEXPORT jboolean JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_login
        (JNIEnv *env, jclass clz, jstring devUUID) {
    string uuid = ConvertToStdString(env, devUUID);
    int code = DauProxyApi::GetInstance()->ProxyLogin(uuid);
    PrintLog("ProxyLogin for %s, code=%d\n", uuid.c_str(), code);
    return code < 0 ? JNI_FALSE : JNI_TRUE;
}


JNIEXPORT jboolean JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_logout
        (JNIEnv *env, jclass clz, jstring devUUID) {
    int code = DauProxyApi::GetInstance()->ProxyLogout(ConvertToStdString(env, devUUID));
    return code < 0 ? JNI_FALSE : JNI_TRUE;
}

JNIEXPORT jboolean JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_deleteDevice
        (JNIEnv *env, jclass clz, jstring devUUID) {
    int code = DauProxyApi::GetInstance()->ProxyDeleteDevice(ConvertToStdString(env, devUUID));
    return code < 0 ? JNI_FALSE : JNI_TRUE;
}


JNIEXPORT jboolean JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getDeviceState
        (JNIEnv *env, jclass clz, jstring devUUID, jint maxWaitSeconds) {
    int count = maxWaitSeconds < 0 ? 0 : maxWaitSeconds;
    int code = -1;
    for (int i = 0; i < count + 1; i++) {
        code = DauProxyApi::GetInstance()->ProxyGetDeviceState(ConvertToStdString(env, devUUID));
        if (code > 0) {
            break;
        }
        sleep(1);
    }
    return code < 0 ? JNI_FALSE : JNI_TRUE;
}


JNIEXPORT jobject JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getDeviceInfo
        (JNIEnv *env, jclass clz, jstring devUUID) {
    _dau_device_info dauInfo = {};
    int code = DauProxyApi::GetInstance()->ProxyGetDeviceInfo(ConvertToStdString(env, devUUID), dauInfo);
    if (code < 0) {
        THROW_VAG_EX(env, "ProxyGetDeviceInfo return error");
        return nullptr;
    }
    return ConvertFromDauDeviceInfo(env, dauInfo);
}

// 获取设备通道信息
JNIEXPORT jobject JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getDevChannelInfo
        (JNIEnv *env, jclass clz, jstring devUUID) {
    const char* methodName = "getDevChannelInfo";

    string sdevUUID = ConvertToStdString(env, devUUID);
    _device_channel_info deviceChannelInfo = {};
    int code = DauProxyApi::GetInstance()->ProxyGetDevChannelInfo(sdevUUID, deviceChannelInfo);
    PrintLog("%s: code=%d\n", methodName, code);
    if (code < 0) {
        THROW_VAG_EX(env, "ProxyGetDevChannelInfo return error");
        return nullptr;
    }
    return ConvertFromDevChannelInfo(env, deviceChannelInfo);
}


JNIEXPORT jstring JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_startStream
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jstring subChannel, jstring streamUrl) {
    string sChannel = ConvertToStdString(env, channel);
    string sdevUUID = ConvertToStdString(env, devUUID);
    string sSubChannel = ConvertToStdString(env, subChannel);
    string sStreamUrl = ConvertToStdString(env, streamUrl);
    string steamUUid;
    int code = DauProxyApi::GetInstance()->ProxyStartStream(sdevUUID, sChannel, sSubChannel, sStreamUrl,
                                                            steamUUid);
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxyStartStream return error");
    return env->NewStringUTF(steamUUid.c_str());
}


JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_stopStream
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jstring subChannel, jstring stream_uuid) {
    int code = DauProxyApi::GetInstance()->ProxyStopStream(ConvertToStdString(env, devUUID),
                                                           ConvertToStdString(env, channel),
                                                           ConvertToStdString(env, subChannel),
                                                           ConvertToStdString(env, stream_uuid));
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxyStopStream return error");
}


JNIEXPORT jobject JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getOsdShowString
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel) {
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    _proxy_showstring_info info{};
    int code = DauProxyApi::GetInstance()->ProxyGetOsdShowString(sdevUUID, schannel, info);
    if (code < 0) {
        THROW_VAG_EX(env, "ProxyGetOsdShowString return error");
        return nullptr;
    }
    return ConvertFromProxyShowStringInfo(env, info);
}

JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_setOsdShowString
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jobject proxyShowStringInfo) {
    ParseProxyShowStringInfoRs pps = ParseProxyShowStringInfo(env, proxyShowStringInfo);
    RETURN_IF_FALSE(pps);
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    for (auto o: pps.info.struStringInfo) {
        PrintLog("iShowString=%d, iStringSize=%d, x=%d, y=%d, name=%s\n",
                 o.iShowString, o.iStringSize, o.iShowStringTopLeftX, o.iShowStringTopLeftY, o.sString);
    }
    int code = DauProxyApi::GetInstance()->ProxySetOsdShowString(sdevUUID, schannel, pps.info);
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxySetOsdShowString return error");
}

JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_ptzControl
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jint command, jint param) {
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    int code = DauProxyApi::GetInstance()->ProxyPtzControl(sdevUUID, schannel, command, param);
    PrintLog("ptzControl returned code=%d\n", code);
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxyPtzControl return error");
}

JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_ptzCruiseControl
(JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jint command, jint cruiseroute, jint cruiseid, jint param) {
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    int code = DauProxyApi::GetInstance()->ProxyPtzCruiseControl(sdevUUID, schannel, command,cruiseroute,cruiseid, param);
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxyPtzCruiseControl return error");
}

void alarmCallBack(uint32_t lCommand, _proxy_dev_info &devinfo, char *pAlarmInfo, uint32_t dwBufLen, void *pUser) {
    if (pAlarmInfo == nullptr) {
        return;
    }

    JNIHandle *h = JNIHandle::GetInstance();
    JNIEnv *env;
    h->jvm->AttachCurrentThread((void **) (&env), nullptr);

    jstring data = env->NewStringUTF(pAlarmInfo);
    printf("%s", data);
    env->CallStaticVoidMethod(h->cls_vagApi, h->mid_vagApi_alarmCallback, data);
    env->DeleteLocalRef(data);


    h->jvm->DetachCurrentThread();

}

JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_setupAlarmChan
        (JNIEnv *env, jclass clz, jstring devUUID, jobject setupAlarm, jstring alarmDataXml) {
    ParseAlarmInfoRs alarmInfoRs = ParseSetupAlarmInfo(env, setupAlarm);
    RETURN_IF_FALSE(alarmInfoRs);

    const char *alarmXml = env->GetStringUTFChars(alarmDataXml, JNI_FALSE);
    if (alarmDataXml == nullptr) {
        THROW_VAG_EX(env, "The alarmDataXml can't be null or empty");
        return;
    }
    string sdevUUID = ConvertToStdString(env, devUUID);
    int code = DauProxyApi::GetInstance()->ProxySetAlarmCallback(sdevUUID, alarmCallBack, nullptr);
    if (code < 0) {
        env->ReleaseStringUTFChars(alarmDataXml, alarmXml);
        THROW_VAG_EX(env, "ProxySetAlarmCallback return error");
        return;
    }

    code = DauProxyApi::GetInstance()->ProxySetupAlarmChan(sdevUUID, &alarmInfoRs.info, alarmXml, strlen(alarmXml));

    env->ReleaseStringUTFChars(alarmDataXml, alarmXml);

    CHECK_CODE_THROW_VAG_EX(env, code, "ProxySetupAlarmChan return error");
}

JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_stopAlarmChan
        (JNIEnv *env, jclass clz, jstring devUUID) {
    string sdevUUID = ConvertToStdString(env, devUUID);
    int code = DauProxyApi::GetInstance()->ProxyStopAlarmChan(sdevUUID);
    CHECK_CODE_THROW_VAG_EX(env, code, "ProxyStopAlarmChan return error");
}


JNIEXPORT jstring JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_sdapFindDevice
        (JNIEnv *env, jclass clz, jint devType, jint findTime) {
    string devData;
    int code = DauProxyApi::GetInstance()->ProxySdapFindDevicePlus(_proxy_dev_type(devType), devData, findTime);
    if (code < 0) {
        THROW_VAG_EX(env, "ProxySdapFindDevice return error");
    }
    return env->NewStringUTF(devData.c_str());
}


JNIEXPORT jstring JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getLiveStreamUrl
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jstring subChannel) {
    string streamUrl;
    int code = DauProxyApi::GetInstance()->ProxyGetLiveStreamUrl(ConvertToStdString(env, devUUID),
                                                                 ConvertToStdString(env, channel),
                                                                 ConvertToStdString(env, subChannel), streamUrl);
    if (code < 0) {
        THROW_VAG_EX(env, "ProxyGetLiveStreamUrl return error");
    }
    return env->NewStringUTF(streamUrl.c_str());
}

JNIEXPORT jstring JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getVodStreamUrl
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jstring subChannel, jstring beginTime,
         jstring endTime, jint vodType) {
    string streamUrl;
    int code = DauProxyApi::GetInstance()->ProxyGetVodStreamUrl(ConvertToStdString(env, devUUID),
                                                                ConvertToStdString(env, channel),
                                                                ConvertToStdString(env, subChannel),
                                                                ConvertToStdString(env, beginTime),
                                                                ConvertToStdString(env, endTime),
                                                                vodType,
                                                                streamUrl);
    if (code < 0) {
        PrintLog("getVodStreamUrl, error code=%d\n", code);
        THROW_VAG_EX(env, "ProxyGetVodStreamUrl return error");
    }
    return env->NewStringUTF(streamUrl.c_str());
}

JNIEXPORT jobject JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_getPresetInfos
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel) {
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    _ptz_preset_list_info info{};
    int code = DauProxyApi::GetInstance()->ProxyGetPresetInfos(sdevUUID, schannel, info);
    PrintLog("ProxyGetPresetInfos for deviceId=%s, channel=%s,code=%d\n", sdevUUID.c_str(),schannel.c_str(), code);
    if (code < 0) {
        THROW_VAG_EX(env, "ProxyGetPresetInfos return error");
        return nullptr;
    }
    return ConvertFromProxyPtzPresetListInfo(env, info);
}
JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_setPresetName
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jint presetID, jstring presetName) {
    // 将传入的 jstring 转换为 std::string
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    string spresetName = ConvertToStdString(env, presetName);

    // 调用 Proxy_SetPresetName 方法
    int code = DauProxyApi::GetInstance()->ProxySetPresetName(sdevUUID, schannel, presetID, spresetName);

    // 检查返回代码并抛出异常
    CHECK_CODE_THROW_VAG_EX(env, code, "Proxy_SetPresetName return error");
}
JNIEXPORT void JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_makeKeyFrame
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jstring subchannel) {
    // 将传入的 jstring 转换为 std::string
    string sdevUUID = ConvertToStdString(env, devUUID);
    string schannel = ConvertToStdString(env, channel);
    string ssubchannel = ConvertToStdString(env, subchannel);

    // 调用 Proxy_MakeKeyFrame 方法
    int code = DauProxyApi::GetInstance()->ProxyMakeKeyFrame(sdevUUID, schannel, ssubchannel);

    // 检查返回代码并抛出异常
    CHECK_CODE_THROW_VAG_EX(env, code, "Proxy_MakeKeyFrame return error");
}
JNIEXPORT jobject JNICALL Java_com_arcvideo_ocean_vag_jni_VagApi_queryRecordInfos
        (JNIEnv *env, jclass clz, jstring devUUID, jstring channel, jstring subChannel, jstring beginTime,
         jstring endTime, jint vodType) {

    // 转换 jstring 参数为 std::string
    std::string devUUIDStr = ConvertToStdString(env, devUUID);
    std::string channelStr = ConvertToStdString(env, channel);
    std::string subChannelStr = ConvertToStdString(env, subChannel);
    std::string beginTimeStr = ConvertToStdString(env, beginTime);
    std::string endTimeStr = ConvertToStdString(env, endTime);
    // 打印请求参数
    PrintLog("QueryRecordInfos called with parameters: devUUID=%s, channel=%s, subChannel=%s, beginTime=%s, endTime=%s, vodType=%d\n",
             devUUIDStr.c_str(),
             channelStr.c_str(),
             subChannelStr.c_str(),
             beginTimeStr.c_str(),
             endTimeStr.c_str(),
             vodType);
    // 调用代理接口获取记录信息
    _record_list_info info{};
    int code = DauProxyApi::GetInstance()->ProxyQueryRecordInfos(
            devUUIDStr,
            channelStr,
            subChannelStr,
            beginTimeStr,
            endTimeStr,
            vodType,
            info);
    // 打印返回码
    PrintLog("ProxyQueryRecordInfos returned code=%d\n", code);
    // 检查返回值
    if (code < 0) {
        THROW_VAG_EX(env, "ProxyQueryRecordInfos return error");
        return nullptr;
    }
    // 返回转换后的 Record List 信息
    return ConvertFromProxyRecordListInfo(env, info);
}

